#include "objmodel.h"

struct ObjModel::ObjVertex
{
    float x, y, z;
};

struct ObjModel::ObjUv
{
    float u, v;
};

struct ObjModel::ObjFace
{
    int vertex[3];
    int normal[3];
    int uv[3];
};

ObjModel::ObjModel(const char* filename)
{
    _vertices = NULL;
    _numVertices = 0;
    _vboId = 0;
    _texture = NULL;

    read(filename);
    createHwBuffers();
}

ObjModel::~ObjModel()
{
    if (_vertices != NULL) {
        delete[] _vertices;
    }
    if (_texture != NULL) {
        delete _texture;
    }

    glDeleteBuffers(1, (GLuint*)&_vboId);
}

void ObjModel::read(const char* filename)
{
    std::ifstream file(filename);

    std::vector<ObjVertex> vertices;
    std::vector<ObjVertex> normals;
    std::vector<ObjUv> uvs;
    std::vector<ObjFace> faces;

    std::string materialFile;

    if (file.is_open()) {

        std::string line;
        while (!file.eof()) {
            getline(file, line);

            if (line.compare(0, 2, "v ") == 0) {
                ObjVertex v;
                sscanf(line.c_str(), "v %f %f %f", &v.x, &v.y, &v.z);
                vertices.push_back(v);
            }
            else if (line.compare(0, 3, "vn ") == 0) {
                ObjVertex vn;
                sscanf(line.c_str(), "vn %f %f %f", &vn.x, &vn.y, &vn.z);
                normals.push_back(vn);
            }
            else if (line.compare(0, 3, "vt ") == 0) {
                ObjUv uv;
                sscanf(line.c_str(), "vt %f %f", &uv.u, &uv.v);
                uvs.push_back(uv);
            }
            else if (line.compare(0, 2, "f ") == 0) {
                ObjFace face;
                sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d",
                       &face.vertex[0], &face.uv[0], &face.normal[0],
                       &face.vertex[1], &face.uv[1], &face.normal[1],
                       &face.vertex[2], &face.uv[2], &face.normal[2]);
                faces.push_back(face);
            }
            else if (line.compare(0, 7, "usemtl ") == 0) {
                // TODO
            }
            else if (line.compare(0, 7, "mtllib ") == 0) {
                char buf[255];
                memset(buf, 0, sizeof(buf));
                sscanf(line.c_str(), "mtllib %s", buf);

                materialFile.append("..\\..\\models\\");
                materialFile.append(buf);
            }
        }

        // Read .mtl file
        readMaterial(materialFile.c_str());


        // Construct the final interleaved vertices

        _numVertices = faces.size() * 3;
        _vertices = new Vertex[_numVertices];

        int n = 0;
        for (std::vector<ObjFace>::iterator i = faces.begin();
                i != faces.end(); ++i) {

            for (int j = 0; j < 3; ++j) {
                int iVert = i->vertex[j] - 1;
                int iUv = i->uv[j] - 1;
                int iNormal = i->normal[j] - 1;

                _vertices[n].x = vertices.at(iVert).x;
                _vertices[n].y = vertices.at(iVert).y;
                _vertices[n].z = vertices.at(iVert).z;

                _vertices[n].u = uvs.at(iUv).u;
                _vertices[n].v = uvs.at(iUv).v;

                _vertices[n].nx = normals.at(iNormal).x;
                _vertices[n].ny = normals.at(iNormal).y;
                _vertices[n].nz = normals.at(iNormal).z;

                n++;
            }
        }

    }
}

void ObjModel::readMaterial(const char* filename)
{
    std::ifstream file(filename);

    if (file.is_open()) {
        std::string line;
        while (!file.eof()) {
            getline(file, line);

            if (line.compare(0, 7, "map_Kd ") == 0) {    // diffuse texture
                char buf[255];
                memset(buf, 0, sizeof(buf));
                sscanf(line.c_str(), "map_Kd %s", buf);

                std::string textureFile("..\\..\\models\\textures\\");
                textureFile.append(buf);

                _texture = new Texture(textureFile.c_str());
            }
        }
    }
}

void ObjModel::createHwBuffers()
{
    glGenBuffers(1, (GLuint*)&_vboId);
    glBindBuffer(GL_ARRAY_BUFFER, _vboId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * _numVertices, _vertices,
                 GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

int ObjModel::getVboId() const
{
    return _vboId;
}

int ObjModel::getNumVertices() const
{
    return _numVertices;
}

const Texture& ObjModel::getTexture() const
{
    return *_texture;
}
